package mapper;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import mapper.base.IPlugin;
import mapper.config.MapperApiConfigure;
import mapper.datasource.DataSourceContextHolder;
import mapper.datasource.DatasourceConfigure;
import mapper.entity.SqlInfo;
import mapper.model.ResponseData;
import mapper.session.MapperApiSqlSessionFactory;
import mapper.utils.StringUtil;
import org.apache.el.util.ReflectionUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.*;
import java.sql.Date;
import java.util.*;
import java.util.regex.Pattern;

@Slf4j
@Component
public class SqlExecuorPlugin implements IPlugin {

    @Value("${mapper-api.context:}")
    private String context;

    @Autowired
    private MapperApiConfigure mapperApiConfigure;

    @Override
    public ResponseEntity execute(
            Map<String, String> pathVar,
            Map<String, Object> param,
            Map<String, Object> data, 
            MultipartFile file,
            Map<String, String> extendParam,
            ResponseData responseData,
            HttpServletRequest request, HttpServletResponse response) throws Throwable {
        log.info("SqlExecuorPlugin execute begin.......................");
        log.info("Url context==============> " + context);

        if(!extendParam.containsKey("sql") || StringUtil.isEmpty(extendParam.get("sql"))) {
            throw new SQLException("SqlExecuorPlugin Error: No sql has been config!!!");
        }
        String dsName = DataSourceContextHolder.DEFAULT_DATASOURCE;
        if(extendParam.containsKey("datasource") && StringUtil.isNotEmpty(extendParam.get("datasource"))) {
            dsName = extendParam.get("datasource");
        }

        SqlSessionFactory sessionFactory = MapperApiSqlSessionFactory.buildSqlSessionFactory(dsName);

        SqlSession session = sessionFactory.openSession();

        String sql = extendParam.get("sql");

        Object result = null;
        if(Pattern.compile("^(insert)", Pattern.MULTILINE).matcher(sql).matches()) {
            result = session.insert(sql, param);
        }

        else if(Pattern.compile("^(delete)", Pattern.MULTILINE).matcher(sql).matches()) {
            result = session.delete(sql, param);
        }

        else if(Pattern.compile("^(update)", Pattern.MULTILINE).matcher(sql).matches()) {
            result = session.update(sql, param);
        }

        else {
            result = session.selectList("mapper.dao.TestDao.findTestList");
//            result = session.selectList("mapper.dao.TestDao.findTestList", new SqlInfo() {{
//                setSql(sql);
//                setParam(param);
//            }});
            /*
            PreparedStatement preparedstatement = getPrepareStatement(DataSourceContextHolder.get(dsName), session.getConnection(), sql);
            ResultSet rs = preparedstatement.executeQuery();

            List<Map<String, Object>> list = new ArrayList<>();
            ResultSetMetaData rsmd = rs.getMetaData();
            while ( rs.next() )
            {
                Map<String, Object> mateData = getMetaData(rs, rsmd, Map.class);
                list.add( mateData );
            }
            rs.close();
            result = list;
             */
        }
        log.info("SqlExecuorPlugin execute end!!!");

        return new ResponseEntity<ResponseData>(new ResponseData(0, "This is SqlExecuorPlugin", result), HttpStatus.OK);
    }

    public static PreparedStatement getPrepareStatement(DataSource datasource, Connection conn, String sql) throws SQLException {
        return getPrepareStatement(datasource, conn, sql, null, false);
    }

    public static PreparedStatement getPrepareStatement(DataSource datasource, Connection conn, String sql, String[] keys, boolean generatedKeys) throws SQLException {
        try {
            if(generatedKeys) {
                if(keys != null && keys.length>0) {
                    return conn.prepareStatement(sql, keys);
                }
                return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            }
            return conn.prepareStatement(sql);
        } catch (SQLException throwables) {
            if (conn.isClosed()) {
                conn = datasource.getConnection();
            }
            else {
                conn.close();
                conn = datasource.getConnection();
            }
            return getPrepareStatement(datasource, conn, sql, keys, generatedKeys);
        }
    }

    /**
     * 判断一个类是JAVA类型还是用户定义类型
     * @param clz
     * @return
     */
    public static boolean isJavaClass(Class<?> clz) {
        return clz != null && clz.getClassLoader() == null;
    }

    public static <E> E getMetaData(ResultSet rs, ResultSetMetaData rsmd, Class<E> returnType) throws SQLException, InstantiationException, IllegalAccessException {
        int columnCount = rsmd.getColumnCount();
        E result = null;
        Map<String, Object> mapMetaData = new HashMap<String, Object>();

        if(!isJavaClass(returnType)) {
            result = returnType.newInstance();
        }

        for ( int i = 0; i < columnCount; i++ )
        {
            try
            {
                String columnName = rsmd.getColumnLabel( i + 1 );
                Object columnValue = null;
                try{
                    columnValue = rs.getObject( columnName );
                } catch (SQLException ex){}
                if(returnType.isPrimitive()
                        || returnType == Date.class
                        || returnType == String.class
                        || returnType == BigDecimal.class
                        || returnType == Integer.class
                        || returnType == Long.class
                        || returnType == Float.class
                        || returnType == Double.class
                        || returnType == Number.class) {

                    if(returnType == String.class) {
                        result = (E)StringUtil.cast(returnType, columnValue.toString());
                    }

                    else {
                        result = (E) columnValue;
                    }
                }
                else if(returnType == UUID.class) {

                    result = (E)UUID.fromString(columnValue.toString());
                }
                else if(returnType == Map.class) {
                    mapMetaData.put(columnName, columnValue);
                }
            }

            catch ( Exception e )
            {
                log.error(e.getMessage());
            }
        }

        if(returnType == Map.class) {
            result = (E) mapMetaData;
        }

        return result;
    }
}
